<?php
/**
 * The api library of zentaopms.
 *
 * @copyright   Copyright 2009-2015 禅道软件（青岛）有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
 * @license     ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
 * @author      Chunsheng Wang <chunsheng@cnezsoft.com>
 * @package     API
 * @version     $Id: api.class.php 4132 2013-01-18 02:30:05Z wwccss $
 * @link        http://www.zentao.net
 */
class ztclient
{
    public $configFile;
    public $zentao;
    public $agent;
    public $session;

    /**
     * The construce function.
     *
     * @param  string $zentaoRoot   the zentao root url
     * @param  string $account      the login account
     * @param  string $password     the passwod
     * @access public
     * @return void
     */
    public function __construct($zentaoRoot = '', $account = '', $password = '')
    {
        $this->agent = new snoopy();

        /* Assign to $this->zentao. */
        $this->zentao->root     = rtrim($zentaoRoot, '/') . '/';
        $this->zentao->account  = $account;
        $this->zentao->password = md5($password);

        /* Load the remote settings. */
        $config = $this->getRemoteConfig();
        foreach($config as $key => $value) $this->zentao->$key = $value;

        $this->startSession();
        $this->login();
    }

    /**
     * Get the settings through getconfig api from remote.
     *
     * @access private
     * @return object
     */
    private function getRemoteConfig()
    {
        $url = $this->zentao->root  . 'index.php?mode=getconfig';
        $this->agent->fetchText($url);
        $config = json_decode($this->agent->results);
        return $config;
    }

    /**
     * Set the session api.
     *
     * @access private
     * @return string the session api url.
     */
    private function setSessionAPI()
    {
        return $this->setControlAPI('api', 'getsessionid');
    }

    /**
     * Set the login api.
     *
     * @access private
     * @return string the login api url.
     */
    private function setLoginAPI()
    {
        return $this->setControlAPI('user', 'login');
    }

    /**
     * Set the method api.
     *
     * @param  string $module   the module name
     * @param  string $method   the methhod name
     * @param  string $vars     the vars to passwd
     * @access private
     * @return string the control api string.
     */
    private function setControlAPI($module, $method, $vars = '')
    {
        if($this->zentao->requestType == 'GET')
        {
            $controlAPI = $this->appendSession($this->zentao->root);
            $controlAPI .= "&{$this->zentao->moduleVar}=$module&{$this->zentao->methodVar}=$method&{$this->zentao->viewVar}=json$vars";
        }
        elseif($this->zentao->requestType == 'PATH_INFO')
        {
            $controlAPI = $this->zentao->root . $module . $this->zentao->requestFix . $method . $this->zentao->requestFix;
            if($vars)
            {
                parse_str($vars, $vars);
                foreach($vars as $var) $controlAPI .= $var . $this->zentao->requestFix;
            }
            $controlAPI = rtrim($controlAPI, $this->zentao->requestFix) . '.json';
            $controlAPI = $this->appendSession($controlAPI);
        }
        return $controlAPI;
    }

    /**
     * Set the method api.
     *
     * @param  string $module   the module name
     * @param  string $method   the methhod name
     * @param  string $vars     the vars to passed
     * @access private
     * @return string the model api string.
     */
    private function setModelAPI($module, $method, $vars = '')
    {
        $vars = str_replace('&', ',', $vars);
        if($this->zentao->requestType == 'GET')
        {
            $modelAPI = $this->appendSession($this->zentao->root);
            $modelAPI .= "&{$this->zentao->moduleVar}=api&{$this->zentao->methodVar}=getModel&{$this->zentao->viewVar}=json";
            $modelAPI .="&module=$module&method=$method&params=$vars";
        }
        elseif($this->zentao->requestType == 'PATH_INFO')
        {
            $modelAPI  = $this->zentao->root . 'api' . $this->zentao->requestFix . 'getmodel' . $this->zentao->requestFix;
            $modelAPI .= $module . $this->zentao->requestFix . $method . $this->zentao->requestFix;
            $modelAPI .= $vars;
            $modelAPI  = rtrim($modelAPI, $this->zentao->requestFix) . '.json';
            $modelAPI  = $this->appendSession($modelAPI);
        }
        return $modelAPI;
    }

    /**
     * Start session
     *
     * @access public
     * @return void
     */
    public function startSession()
    {
        $this->session = null;
        $this->session = $this->httpGet($this->setSessionAPI());
    }

    /**
     * Login.
     *
     * @access public
     * @return void
     */
    public function login()
    {
        $loginAPI = $this->setLoginAPI();
        $authHash = md5($this->zentao->password . $this->session->rand);
        $loginAPI .= "&account={$this->zentao->account}&password=$authHash";
        $this->httpGet($loginAPI);
    }

    /**
     * Fetch one method of a module's control.
     *
     * @param  string $module   the module name
     * @param  string $method   the methhod name
     * @param  string $vars     the vars to passwd
     * @access public
     * @return void
     */
    public function fetch($module, $method = 'index', $vars = '')
    {
        return $this->httpGet($this->setControlAPI($module, $method, $vars));
    }

    /**
     * Fetch one method of a module's model.
     *
     * @param  string $module   the module name
     * @param  string $method   the methhod name
     * @param  string $vars     the vars to passwd
     * @access public
     * @return void
     */
    public function fetchModel($module, $method, $vars = '')
    {
        return $this->httpGet($this->setModelAPI($module, $method, $vars));
    }

    /**
     * Get a api and check it.
     *
     * @param  string $url
     * @access private
     * @return bool
     */
    private function httpGet($url)
    {
        $this->agent->fetch($url);
        $result = json_decode($this->agent->results);
        if($result->status != 'success') return false;
        if(isset($result->data) and md5($result->data) != $result->md5) return false;
        if(isset($result->data)) return json_decode($result->data);
        return true;
    }

    /**
     * Fetch one method of a module's control.
     *
     * @param  string $module   the module name
     * @param  string $method   the methhod name
     * @param  array  $vars     the vars to passwd
     * @access public
     * @return void
     */
    public function post($module, $method = 'index', $vars = array())
    {
        return $this->httpPost($this->setControlAPI($module, $method), $vars);
    }

    /**
     * Post.
     *
     * @param  string $url
     * @access private
     * @return bool
     */
    private function httpPost($url, $vars)
    {
        $this->agent->submit($url, $vars);
        $result = json_decode($this->agent->results);
        if($result->status != 'success') return false;
        if(isset($result->data) and md5($result->data) != $result->md5) return false;
        if(isset($result->data)) return json_decode($result->data);
        return true;
    }

    /**
     * Append session param to the url.
     *
     * @param  string $url
     * @access private
     * @return string
     */
    private function appendSession($url)
    {
        if(strrpos($url, '&') === false) $url .= '?';
        if($this->session) $url .= '&' . $this->session->sessionName . '=' . $this->session->sessionID;
        return str_replace('?&', '?', $url);
    }
}
?>
<?php

/*************************************************

Snoopy - the PHP net client
Author: Monte Ohrt <monte@ispi.net>
Copyright (c): 1999-2008 New Digital Group, all rights reserved
Version: 1.2.4

 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

You may contact the author of Snoopy by e-mail at:
monte@ohrt.com

The latest version of Snoopy can be obtained from:
http://snoopy.sourceforge.net/

*************************************************/

class Snoopy
{
    /**** Public variables ****/

    /* user definable vars */

    var $host			=	"www.php.net";		// host name we are connecting to
    var $port			=	80;					// port we are connecting to
    var $proxy_host		=	"";					// proxy host to use
    var $proxy_port		=	"";					// proxy port to use
    var $proxy_user		=	"";					// proxy user to use
    var $proxy_pass		=	"";					// proxy password to use

    var $agent			=	"Snoopy v1.2.4";	// agent we masquerade as
    var	$referer		=	"";					// referer info to pass
    var $cookies		=	array();			// array of cookies to pass
    // $cookies["username"]="joe";
    var	$rawheaders		=	array();			// array of raw headers to send
    // $rawheaders["Content-type"]="text/html";

    var $maxredirs		=	5;					// http redirection depth maximum. 0 = disallow
    var $lastredirectaddr	=	"";				// contains address of last redirected address
    var	$offsiteok		=	true;				// allows redirection off-site
    var $maxframes		=	0;					// frame content depth maximum. 0 = disallow
    var $expandlinks	=	true;				// expand links to fully qualified URLs.
    // this only applies to fetchlinks()
    // submitlinks(), and submittext()
    var $passcookies	=	true;				// pass set cookies back through redirects
    // NOTE: this currently does not respect
    // dates, domains or paths.

    var	$user			=	"";					// user for http authentication
    var	$pass			=	"";					// password for http authentication

    // http accept types
    var $accept			=	"image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*";

    var $results		=	"";					// where the content is put

    var $error			=	"";					// error messages sent here
    var	$response_code	=	"";					// response code returned from server
    var	$headers		=	array();			// headers returned from server sent here
    var	$maxlength		=	500000;				// max return data length (body)
    var $read_timeout	=	0;					// timeout on read operations, in seconds
    // supported only since PHP 4 Beta 4
    // set to 0 to disallow timeouts
    var $timed_out		=	false;				// if a read operation timed out
    var	$status			=	0;					// http request status

    var $temp_dir		=	"/tmp";				// temporary directory that the webserver
    // has permission to write to.
    // under Windows, this should be C:\temp

    var	$curl_path		=	"/usr/local/bin/curl";
    // Snoopy will use cURL for fetching
    // SSL content if a full system path to
    // the cURL binary is supplied here.
    // set to false if you do not have
    // cURL installed. See http://curl.haxx.se
    // for details on installing cURL.
    // Snoopy does *not* use the cURL
    // library functions built into php,
    // as these functions are not stable
    // as of this Snoopy release.

    /**** Private variables ****/

    var	$_maxlinelen	=	4096;				// max line length (headers)

    var $_httpmethod	=	"GET";				// default http request method
    var $_httpversion	=	"HTTP/1.0";			// default http request version
    var $_submit_method	=	"POST";				// default submit method
    var $_submit_type	=	"application/x-www-form-urlencoded";	// default submit type
    var $_mime_boundary	=   "";					// MIME boundary for multipart/form-data submit type
    var $_redirectaddr	=	false;				// will be set if page fetched is a redirect
    var $_redirectdepth	=	0;					// increments on an http redirect
    var $_frameurls		= 	array();			// frame src urls
    var $_framedepth	=	0;					// increments on frame depth

    var $_isproxy		=	false;				// set if using a proxy server
    var $_fp_timeout	=	30;					// timeout for socket connection

/*======================================================================*\
    Function:	fetch
    Purpose:	fetch the contents of a web page
                (and possibly other protocols in the
                future like ftp, nntp, gopher, etc.)
    Input:		$URI	the location of the page to fetch
    Output:		$this->results	the output text from the fetch
\*======================================================================*/

    function fetch($URI)
    {

        //preg_match("|^([^:]+)://([^:/]+)(:[\d]+)*(.*)|",$URI,$URI_PARTS);
        $URI_PARTS = parse_url($URI);
        if (!empty($URI_PARTS["user"]))
            $this->user = $URI_PARTS["user"];
        if (!empty($URI_PARTS["pass"]))
            $this->pass = $URI_PARTS["pass"];
        if (empty($URI_PARTS["query"]))
            $URI_PARTS["query"] = '';
        if (empty($URI_PARTS["path"]))
            $URI_PARTS["path"] = '';

        switch(strtolower($URI_PARTS["scheme"]))
        {
        case "http":
            $this->host = $URI_PARTS["host"];
            if(!empty($URI_PARTS["port"]))
                $this->port = $URI_PARTS["port"];
            if($this->_connect($fp))
            {
                if($this->_isproxy)
                {
                    // using proxy, send entire URI
                    $this->_httprequest($URI,$fp,$URI,$this->_httpmethod);
                }
                else
                {
                    $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : "");
                    // no proxy, send only the path
                    $this->_httprequest($path, $fp, $URI, $this->_httpmethod);
                }

                $this->_disconnect($fp);

                if($this->_redirectaddr)
                {
                    /* url was redirected, check if we've hit the max depth */
                    if($this->maxredirs > $this->_redirectdepth)
                    {
                        // only follow redirect if it's on this site, or offsiteok is true
                        if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok)
                        {
                            /* follow the redirect */
                            $this->_redirectdepth++;
                            $this->lastredirectaddr=$this->_redirectaddr;
                            $this->fetch($this->_redirectaddr);
                        }
                    }
                }

                if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0)
                {
                    $frameurls = $this->_frameurls;
                    $this->_frameurls = array();

                    foreach($frameurls as $frameurl)
                    {
                        if($this->_framedepth >= $this->maxframes) break;

                        $this->fetch($frameurl);
                        $this->_framedepth++;
                    }
                }
            }
            else
            {
                return false;
            }
            return true;
            break;
        case "https":
            if(!$this->curl_path)
                return false;
            if(function_exists("is_executable"))
                if (!is_executable($this->curl_path))
                    return false;
            $this->host = $URI_PARTS["host"];
            if(!empty($URI_PARTS["port"]))
                $this->port = $URI_PARTS["port"];
            if($this->_isproxy)
            {
                // using proxy, send entire URI
                $this->_httpsrequest($URI,$URI,$this->_httpmethod);
            }
            else
            {
                $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : "");
                // no proxy, send only the path
                $this->_httpsrequest($path, $URI, $this->_httpmethod);
            }

            if($this->_redirectaddr)
            {
                /* url was redirected, check if we've hit the max depth */
                if($this->maxredirs > $this->_redirectdepth)
                {
                    // only follow redirect if it's on this site, or offsiteok is true
                    if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok)
                    {
                        /* follow the redirect */
                        $this->_redirectdepth++;
                        $this->lastredirectaddr=$this->_redirectaddr;
                        $this->fetch($this->_redirectaddr);
                    }
                }
            }

            if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0)
            {
                $frameurls = $this->_frameurls;
                $this->_frameurls = array();

                foreach($frameurls as $frameurl)
                {
                    if($this->_framedepth < $this->maxframes)
                    {
                        $this->fetch($frameurl);
                        $this->_framedepth++;
                    }
                    else
                    {
                        break;
                    }
                }
            }
            return true;
            break;
        default:
            // not a valid protocol
            $this->error	=	'Invalid protocol "'.$URI_PARTS["scheme"].'"\n';
            return false;
            break;
        }
        return true;
    }

/*======================================================================*\
    Function:	submit
    Purpose:	submit an http form
    Input:		$URI	the location to post the data
                $formvars	the formvars to use.
                    format: $formvars["var"] = "val";
                $formfiles  an array of files to submit
                    format: $formfiles["var"] = "/dir/filename.ext";
    Output:		$this->results	the text output from the post
\*======================================================================*/

    function submit($URI, $formvars="", $formfiles="")
    {
        unset($postdata);

        $postdata = $this->_prepare_post_body($formvars, $formfiles);

        $URI_PARTS = parse_url($URI);
        if (!empty($URI_PARTS["user"]))
            $this->user = $URI_PARTS["user"];
        if (!empty($URI_PARTS["pass"]))
            $this->pass = $URI_PARTS["pass"];
        if (empty($URI_PARTS["query"]))
            $URI_PARTS["query"] = '';
        if (empty($URI_PARTS["path"]))
            $URI_PARTS["path"] = '';

        switch(strtolower($URI_PARTS["scheme"]))
        {
        case "http":
            $this->host = $URI_PARTS["host"];
            if(!empty($URI_PARTS["port"]))
                $this->port = $URI_PARTS["port"];
            if($this->_connect($fp))
            {
                if($this->_isproxy)
                {
                    // using proxy, send entire URI
                    $this->_httprequest($URI,$fp,$URI,$this->_submit_method,$this->_submit_type,$postdata);
                }
                else
                {
                    $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : "");
                    // no proxy, send only the path
                    $this->_httprequest($path, $fp, $URI, $this->_submit_method, $this->_submit_type, $postdata);
                }

                $this->_disconnect($fp);

                if($this->_redirectaddr)
                {
                    /* url was redirected, check if we've hit the max depth */
                    if($this->maxredirs > $this->_redirectdepth)
                    {
                        if(!preg_match("|^".$URI_PARTS["scheme"]."://|", $this->_redirectaddr))
                            $this->_redirectaddr = $this->_expandlinks($this->_redirectaddr,$URI_PARTS["scheme"]."://".$URI_PARTS["host"]);

                        // only follow redirect if it's on this site, or offsiteok is true
                        if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok)
                        {
                            /* follow the redirect */
                            $this->_redirectdepth++;
                            $this->lastredirectaddr=$this->_redirectaddr;
                            if( strpos( $this->_redirectaddr, "?" ) > 0 )
                                $this->fetch($this->_redirectaddr); // the redirect has changed the request method from post to get
                            else
                                $this->submit($this->_redirectaddr,$formvars, $formfiles);
                        }
                    }
                }

                if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0)
                {
                    $frameurls = $this->_frameurls;
                    $this->_frameurls = array();

                    foreach($frameurls as $frameurl)
                    {
                        if($this->_framedepth < $this->maxframes)
                        {
                            $this->fetch($frameurl);
                            $this->_framedepth++;
                        }
                        else
                            break;
                    }
                }

            }
            else
            {
                return false;
            }
            return true;
            break;
        case "https":
            if(!$this->curl_path)
                return false;
            if(function_exists("is_executable"))
                if (!is_executable($this->curl_path))
                    return false;
            $this->host = $URI_PARTS["host"];
            if(!empty($URI_PARTS["port"]))
                $this->port = $URI_PARTS["port"];
            if($this->_isproxy)
            {
                // using proxy, send entire URI
                $this->_httpsrequest($URI, $URI, $this->_submit_method, $this->_submit_type, $postdata);
            }
            else
            {
                $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : "");
                // no proxy, send only the path
                $this->_httpsrequest($path, $URI, $this->_submit_method, $this->_submit_type, $postdata);
            }

            if($this->_redirectaddr)
            {
                /* url was redirected, check if we've hit the max depth */
                if($this->maxredirs > $this->_redirectdepth)
                {
                    if(!preg_match("|^".$URI_PARTS["scheme"]."://|", $this->_redirectaddr))
                        $this->_redirectaddr = $this->_expandlinks($this->_redirectaddr,$URI_PARTS["scheme"]."://".$URI_PARTS["host"]);

                    // only follow redirect if it's on this site, or offsiteok is true
                    if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok)
                    {
                        /* follow the redirect */
                        $this->_redirectdepth++;
                        $this->lastredirectaddr=$this->_redirectaddr;
                        if( strpos( $this->_redirectaddr, "?" ) > 0 )
                            $this->fetch($this->_redirectaddr); // the redirect has changed the request method from post to get
                        else
                            $this->submit($this->_redirectaddr,$formvars, $formfiles);
                    }
                }
            }

            if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0)
            {
                $frameurls = $this->_frameurls;
                $this->_frameurls = array();

                foreach($frameurls as $frameurl)
                {
                    if($this->_framedepth < $this->maxframes)
                    {
                        $this->fetch($frameurl);
                        $this->_framedepth++;
                    }
                    else
                        break;
                }
            }
            return true;
            break;

        default:
            // not a valid protocol
            $this->error	=	'Invalid protocol "'.$URI_PARTS["scheme"].'"\n';
            return false;
            break;
        }
        return true;
    }

/*======================================================================*\
    Function:	fetchlinks
    Purpose:	fetch the links from a web page
    Input:		$URI	where you are fetching from
    Output:		$this->results	an array of the URLs
\*======================================================================*/

    function fetchlinks($URI)
    {
        if ($this->fetch($URI))
        {
            if($this->lastredirectaddr)
                $URI = $this->lastredirectaddr;
            if(is_array($this->results))
            {
                for($x=0;$x<count($this->results);$x++)
                    $this->results[$x] = $this->_striplinks($this->results[$x]);
            }
            else
                $this->results = $this->_striplinks($this->results);

            if($this->expandlinks)
                $this->results = $this->_expandlinks($this->results, $URI);
            return true;
        }
        else
            return false;
    }

/*======================================================================*\
    Function:	fetchform
    Purpose:	fetch the form elements from a web page
    Input:		$URI	where you are fetching from
    Output:		$this->results	the resulting html form
\*======================================================================*/

    function fetchform($URI)
    {

        if ($this->fetch($URI))
        {

            if(is_array($this->results))
            {
                for($x=0;$x<count($this->results);$x++)
                    $this->results[$x] = $this->_stripform($this->results[$x]);
            }
            else
                $this->results = $this->_stripform($this->results);

            return true;
        }
        else
            return false;
    }


/*======================================================================*\
    Function:	fetchtext
    Purpose:	fetch the text from a web page, stripping the links
    Input:		$URI	where you are fetching from
    Output:		$this->results	the text from the web page
\*======================================================================*/

    function fetchtext($URI)
    {
        if($this->fetch($URI))
        {
            if(is_array($this->results))
            {
                for($x=0;$x<count($this->results);$x++)
                    $this->results[$x] = $this->_striptext($this->results[$x]);
            }
            else
                $this->results = $this->_striptext($this->results);
            return true;
        }
        else
            return false;
    }

/*======================================================================*\
    Function:	submitlinks
    Purpose:	grab links from a form submission
    Input:		$URI	where you are submitting from
    Output:		$this->results	an array of the links from the post
\*======================================================================*/

    function submitlinks($URI, $formvars="", $formfiles="")
    {
        if($this->submit($URI,$formvars, $formfiles))
        {
            if($this->lastredirectaddr)
                $URI = $this->lastredirectaddr;
            if(is_array($this->results))
            {
                for($x=0;$x<count($this->results);$x++)
                {
                    $this->results[$x] = $this->_striplinks($this->results[$x]);
                    if($this->expandlinks)
                        $this->results[$x] = $this->_expandlinks($this->results[$x],$URI);
                }
            }
            else
            {
                $this->results = $this->_striplinks($this->results);
                if($this->expandlinks)
                    $this->results = $this->_expandlinks($this->results,$URI);
            }
            return true;
        }
        else
            return false;
    }

/*======================================================================*\
    Function:	submittext
    Purpose:	grab text from a form submission
    Input:		$URI	where you are submitting from
    Output:		$this->results	the text from the web page
\*======================================================================*/

    function submittext($URI, $formvars = "", $formfiles = "")
    {
        if($this->submit($URI,$formvars, $formfiles))
        {
            if($this->lastredirectaddr)
                $URI = $this->lastredirectaddr;
            if(is_array($this->results))
            {
                for($x=0;$x<count($this->results);$x++)
                {
                    $this->results[$x] = $this->_striptext($this->results[$x]);
                    if($this->expandlinks)
                        $this->results[$x] = $this->_expandlinks($this->results[$x],$URI);
                }
            }
            else
            {
                $this->results = $this->_striptext($this->results);
                if($this->expandlinks)
                    $this->results = $this->_expandlinks($this->results,$URI);
            }
            return true;
        }
        else
            return false;
    }



/*======================================================================*\
    Function:	set_submit_multipart
    Purpose:	Set the form submission content type to
                multipart/form-data
\*======================================================================*/
    function set_submit_multipart()
    {
        $this->_submit_type = "multipart/form-data";
    }


/*======================================================================*\
    Function:	set_submit_normal
    Purpose:	Set the form submission content type to
                application/x-www-form-urlencoded
\*======================================================================*/
    function set_submit_normal()
    {
        $this->_submit_type = "application/x-www-form-urlencoded";
    }




/*======================================================================*\
    Private functions
\*======================================================================*/


/*======================================================================*\
    Function:	_striplinks
    Purpose:	strip the hyperlinks from an html document
    Input:		$document	document to strip.
    Output:		$match		an array of the links
\*======================================================================*/

    function _striplinks($document)
    {
        preg_match_all("'<\s*a\s.*?href\s*=\s*			# find <a href=
            ([\"\'])?					# find single or double quote
            (?(1) (.*?)\\1 | ([^\s\>]+))		# if quote found, match up to next matching
            # quote, otherwise match up to next space
            'isx",$document,$links);


        // catenate the non-empty matches from the conditional subpattern

        foreach($links[2] as $key => $val)
        {
            if(!empty($val))
                $match[] = $val;
        }

        foreach($links[3] as $key => $val)
        {
            if(!empty($val))
                $match[] = $val;
        }

        // return the links
        return $match;
    }

/*======================================================================*\
    Function:	_stripform
    Purpose:	strip the form elements from an html document
    Input:		$document	document to strip.
    Output:		$match		an array of the links
\*======================================================================*/

    function _stripform($document)
    {
        preg_match_all("'<\/?(FORM|INPUT|SELECT|TEXTAREA|(OPTION))[^<>]*>(?(2)(.*(?=<\/?(option|select)[^<>]*>[\r\n]*)|(?=[\r\n]*))|(?=[\r\n]*))'Usi",$document,$elements);

        // catenate the matches
        $match = implode("\r\n",$elements[0]);

        // return the links
        return $match;
    }



/*======================================================================*\
    Function:	_striptext
    Purpose:	strip the text from an html document
    Input:		$document	document to strip.
    Output:		$text		the resulting text
\*======================================================================*/

    function _striptext($document)
    {

        // I didn't use preg eval (//e) since that is only available in PHP 4.0.
        // so, list your entities one by one here. I included some of the
        // more common ones.

        $search = array("'<script[^>]*?>.*?</script>'si",	// strip out javascript
            "'<[\/\!]*?[^<>]*?>'si",			// strip out html tags
            "'([\r\n])[\s]+'",					// strip out white space
            "'&(quot|#34|#034|#x22);'i",		// replace html entities
            "'&(amp|#38|#038|#x26);'i",			// added hexadecimal values
            "'&(lt|#60|#060|#x3c);'i",
            "'&(gt|#62|#062|#x3e);'i",
            "'&(nbsp|#160|#xa0);'i",
            "'&(iexcl|#161);'i",
            "'&(cent|#162);'i",
            "'&(pound|#163);'i",
            "'&(copy|#169);'i",
            "'&(reg|#174);'i",
            "'&(deg|#176);'i",
            "'&(#39|#039|#x27);'",
                "'&(euro|#8364);'i",				// europe
                "'&a(uml|UML);'",					// german
                "'&o(uml|UML);'",
                "'&u(uml|UML);'",
                "'&A(uml|UML);'",
                "'&O(uml|UML);'",
                "'&U(uml|UML);'",
                "'&szlig;'i",
        );
        $replace = array(	"",
            "",
            "\\1",
            "\"",
            "&",
            "<",
            ">",
            " ",
            chr(161),
            chr(162),
            chr(163),
            chr(169),
            chr(174),
            chr(176),
            chr(39),
            chr(128),
            "?",
            "?",
            "?",
            "?",
            "?",
            "?",
            "?",
        );

        $text = preg_replace($search,$replace,$document);

        return $text;
    }

/*======================================================================*\
    Function:	_expandlinks
    Purpose:	expand each link into a fully qualified URL
    Input:		$links			the links to qualify
                $URI			the full URI to get the base from
    Output:		$expandedLinks	the expanded links
\*======================================================================*/

    function _expandlinks($links,$URI)
    {

        preg_match("/^[^\?]+/",$URI,$match);

        $match = preg_replace("|/[^\/\.]+\.[^\/\.]+$|","",$match[0]);
        $match = preg_replace("|/$|","",$match);
        $match_part = parse_url($match);
        $match_root =
            $match_part["scheme"]."://".$match_part["host"];

        $search = array( 	"|^http://".preg_quote($this->host)."|i",
            "|^(\/)|i",
            "|^(?!http://)(?!mailto:)|i",
            "|/\./|",
            "|/[^\/]+/\.\./|"
        );

        $replace = array(	"",
            $match_root."/",
            $match."/",
            "/",
            "/"
        );

        $expandedLinks = preg_replace($search,$replace,$links);

        return $expandedLinks;
    }

/*======================================================================*\
    Function:	_httprequest
    Purpose:	go get the http data from the server
    Input:		$url		the url to fetch
                $fp			the current open file pointer
                $URI		the full URI
                $body		body contents to send if any (POST)
    Output:
\*======================================================================*/

    function _httprequest($url,$fp,$URI,$http_method,$content_type="",$body="")
    {
        $cookie_headers = '';
        if($this->passcookies && $this->_redirectaddr)
            $this->setcookies();

        $URI_PARTS = parse_url($URI);
        if(empty($url))
            $url = "/";
        $headers = $http_method." ".$url." ".$this->_httpversion."\r\n";
        if(!empty($this->agent))
            $headers .= "User-Agent: ".$this->agent."\r\n";
        if(!empty($this->host) && !isset($this->rawheaders['Host'])) {
            $headers .= "Host: ".$this->host;
            if(!empty($this->port))
                $headers .= ":".$this->port;
            $headers .= "\r\n";
        }
        if(!empty($this->accept))
            $headers .= "Accept: ".$this->accept."\r\n";
        if(!empty($this->referer))
            $headers .= "Referer: ".$this->referer."\r\n";
        if(!empty($this->cookies))
        {
            if(!is_array($this->cookies))
                $this->cookies = (array)$this->cookies;

            reset($this->cookies);
            if ( count($this->cookies) > 0 ) {
                $cookie_headers .= 'Cookie: ';
                foreach ( $this->cookies as $cookieKey => $cookieVal ) {
                    $cookie_headers .= $cookieKey."=".urlencode($cookieVal)."; ";
                }
                $headers .= substr($cookie_headers,0,-2) . "\r\n";
            }
        }
        if(!empty($this->rawheaders))
        {
            if(!is_array($this->rawheaders))
            {
                $this->rawheaders = (array)$this->rawheaders;
            }
            foreach($this->rawheaders as $headerKey => $headerVal)
            {
                $headers .= $headerKey.": ".$headerVal."\r\n";
            }
        }
        if(!empty($content_type)) {
            $headers .= "Content-type: $content_type";
            if ($content_type == "multipart/form-data")
                $headers .= "; boundary=".$this->_mime_boundary;
            $headers .= "\r\n";
        }
        if(!empty($body))
            $headers .= "Content-length: ".strlen($body)."\r\n";
        if(!empty($this->user) || !empty($this->pass))
            $headers .= "Authorization: Basic ".base64_encode($this->user.":".$this->pass)."\r\n";

        //add proxy auth headers
        if(!empty($this->proxy_user))
            $headers .= 'Proxy-Authorization: ' . 'Basic ' . base64_encode($this->proxy_user . ':' . $this->proxy_pass)."\r\n";


        $headers .= "\r\n";

        // set the read timeout if needed
        if ($this->read_timeout > 0)
            socket_set_timeout($fp, $this->read_timeout);
        $this->timed_out = false;

        fwrite($fp,$headers.$body,strlen($headers.$body));

        $this->_redirectaddr = false;
        unset($this->headers);

        while($currentHeader = fgets($fp,$this->_maxlinelen))
        {
            if ($this->read_timeout > 0 && $this->_check_timeout($fp))
            {
                $this->status=-100;
                return false;
            }

            if($currentHeader == "\r\n")
                break;

            // if a header begins with Location: or URI:, set the redirect
            if(preg_match("/^(Location:|URI:)/i",$currentHeader))
            {
                // get URL portion of the redirect
                preg_match("/^(Location:|URI:)[ ]+(.*)/i",chop($currentHeader),$matches);
                // look for :// in the Location header to see if hostname is included
                if(!preg_match("|\:\/\/|",$matches[2]))
                {
                    // no host in the path, so prepend
                    $this->_redirectaddr = $URI_PARTS["scheme"]."://".$this->host.":".$this->port;
                    // eliminate double slash
                    if(!preg_match("|^/|",$matches[2]))
                        $this->_redirectaddr .= "/".$matches[2];
                    else
                        $this->_redirectaddr .= $matches[2];
                }
                else
                    $this->_redirectaddr = $matches[2];
            }

            if(preg_match("|^HTTP/|",$currentHeader))
            {
                if(preg_match("|^HTTP/[^\s]*\s(.*?)\s|",$currentHeader, $status))
                {
                    $this->status= $status[1];
                }
                $this->response_code = $currentHeader;
            }

            $this->headers[] = $currentHeader;
        }

        $results = '';
        do {
            $_data = fread($fp, $this->maxlength);
            if (strlen($_data) == 0) {
                break;
            }
            $results .= $_data;
        } while(true);

        if ($this->read_timeout > 0 && $this->_check_timeout($fp))
        {
            $this->status=-100;
            return false;
        }

        // check if there is a a redirect meta tag

        if(preg_match("'<meta[\s]*http-equiv[^>]*?content[\s]*=[\s]*[\"\']?\d+;[\s]*URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i",$results,$match))

        {
            $this->_redirectaddr = $this->_expandlinks($match[1],$URI);
    }

    // have we hit our frame depth and is there frame src to fetch?
    if(($this->_framedepth < $this->maxframes) && preg_match_all("'<frame\s+.*src[\s]*=[\'\"]?([^\'\"\>]+)'i",$results,$match))
    {
        $this->results[] = $results;
        for($x=0; $x<count($match[1]); $x++)
            $this->_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS["scheme"]."://".$this->host);
    }
    // have we already fetched framed content?
elseif(is_array($this->results))
    $this->results[] = $results;
// no framed content
else
    $this->results = $results;

return true;
    }

/*======================================================================*\
    Function:	_httpsrequest
    Purpose:	go get the https data from the server using curl
    Input:		$url		the url to fetch
                $URI		the full URI
                $body		body contents to send if any (POST)
    Output:
\*======================================================================*/

    function _httpsrequest($url,$URI,$http_method,$content_type="",$body="")
    {
        if($this->passcookies && $this->_redirectaddr)
            $this->setcookies();

        $headers = array();

        $URI_PARTS = parse_url($URI);
        if(empty($url))
            $url = "/";
        // GET ... header not needed for curl
        //$headers[] = $http_method." ".$url." ".$this->_httpversion;
        if(!empty($this->agent))
            $headers[] = "User-Agent: ".$this->agent;
        if(!empty($this->host))
            if(!empty($this->port))
                $headers[] = "Host: ".$this->host.":".$this->port;
            else
                $headers[] = "Host: ".$this->host;
        if(!empty($this->accept))
            $headers[] = "Accept: ".$this->accept;
        if(!empty($this->referer))
            $headers[] = "Referer: ".$this->referer;
        if(!empty($this->cookies))
        {
            if(!is_array($this->cookies))
                $this->cookies = (array)$this->cookies;

            reset($this->cookies);
            if ( count($this->cookies) > 0 ) {
                $cookie_str = 'Cookie: ';
                foreach ( $this->cookies as $cookieKey => $cookieVal ) {
                    $cookie_str .= $cookieKey."=".urlencode($cookieVal)."; ";
                }
                $headers[] = substr($cookie_str,0,-2);
            }
        }
        if(!empty($this->rawheaders))
        {
            if(!is_array($this->rawheaders))
            {
                $this->rawheaders = (array)$this->rawheaders;
            }
            foreach($this->rawheaders as $headerKey => $headerVal)
            {
                $headers[] = $headerKey.": ".$headerVal;
            }
        }
        if(!empty($content_type)) {
            if ($content_type == "multipart/form-data")
                $headers[] = "Content-type: $content_type; boundary=".$this->_mime_boundary;
            else
                $headers[] = "Content-type: $content_type";
        }
        if(!empty($body))
            $headers[] = "Content-length: ".strlen($body);
        if(!empty($this->user) || !empty($this->pass))
            $headers[] = "Authorization: BASIC ".base64_encode($this->user.":".$this->pass);

        for($curr_header = 0; $curr_header < count($headers); $curr_header++) {
            $safer_header = strtr( $headers[$curr_header], "\"", " " );
            $cmdline_params .= " -H \"".$safer_header."\"";
        }

        if(!empty($body))
            $cmdline_params .= " -d \"$body\"";

        if($this->read_timeout > 0)
            $cmdline_params .= " -m ".$this->read_timeout;

        $headerfile = tempnam($temp_dir, "sno");

        exec($this->curl_path." -k -D \"$headerfile\"".$cmdline_params." \"".escapeshellcmd($URI)."\"",$results,$return);

        if($return)
        {
            $this->error = "Error: cURL could not retrieve the document, error $return.";
            return false;
        }


        $results = implode("\r\n",$results);

        $result_headers = file("$headerfile");

        $this->_redirectaddr = false;
        unset($this->headers);

        for($currentHeader = 0; $currentHeader < count($result_headers); $currentHeader++)
        {

            // if a header begins with Location: or URI:, set the redirect
            if(preg_match("/^(Location: |URI: )/i",$result_headers[$currentHeader]))
            {
                // get URL portion of the redirect
                preg_match("/^(Location: |URI:)\s+(.*)/",chop($result_headers[$currentHeader]),$matches);
                // look for :// in the Location header to see if hostname is included
                if(!preg_match("|\:\/\/|",$matches[2]))
                {
                    // no host in the path, so prepend
                    $this->_redirectaddr = $URI_PARTS["scheme"]."://".$this->host.":".$this->port;
                    // eliminate double slash
                    if(!preg_match("|^/|",$matches[2]))
                        $this->_redirectaddr .= "/".$matches[2];
                    else
                        $this->_redirectaddr .= $matches[2];
                }
                else
                    $this->_redirectaddr = $matches[2];
            }

            if(preg_match("|^HTTP/|",$result_headers[$currentHeader]))
                $this->response_code = $result_headers[$currentHeader];

            $this->headers[] = $result_headers[$currentHeader];
        }

        // check if there is a a redirect meta tag

        if(preg_match("'<meta[\s]*http-equiv[^>]*?content[\s]*=[\s]*[\"\']?\d+;[\s]*URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i",$results,$match))
        {
            $this->_redirectaddr = $this->_expandlinks($match[1],$URI);
    }

    // have we hit our frame depth and is there frame src to fetch?
    if(($this->_framedepth < $this->maxframes) && preg_match_all("'<frame\s+.*src[\s]*=[\'\"]?([^\'\"\>]+)'i",$results,$match))
    {
        $this->results[] = $results;
        for($x=0; $x<count($match[1]); $x++)
            $this->_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS["scheme"]."://".$this->host);
    }
    // have we already fetched framed content?
elseif(is_array($this->results))
    $this->results[] = $results;
// no framed content
else
    $this->results = $results;

unlink("$headerfile");

return true;
    }

/*======================================================================*\
    Function:	setcookies()
    Purpose:	set cookies for a redirection
\*======================================================================*/

    function setcookies()
    {
        for($x=0; $x<count($this->headers); $x++)
        {
            if(preg_match('/^set-cookie:[\s]+([^=]+)=([^;]+)/i', $this->headers[$x],$match))
                $this->cookies[$match[1]] = urldecode($match[2]);
    }
    }


/*======================================================================*\
    Function:	_check_timeout
    Purpose:	checks whether timeout has occurred
    Input:		$fp	file pointer
\*======================================================================*/

    function _check_timeout($fp)
    {
        if ($this->read_timeout > 0) {
            $fp_status = socket_get_status($fp);
            if ($fp_status["timed_out"]) {
                $this->timed_out = true;
                return true;
            }
        }
        return false;
    }

/*======================================================================*\
    Function:	_connect
    Purpose:	make a socket connection
    Input:		$fp	file pointer
\*======================================================================*/

    function _connect(&$fp)
    {
        if(!empty($this->proxy_host) && !empty($this->proxy_port))
        {
            $this->_isproxy = true;

            $host = $this->proxy_host;
            $port = $this->proxy_port;
        }
        else
        {
            $host = $this->host;
            $port = $this->port;
        }

        $this->status = 0;

        if($fp = fsockopen(
            $host,
            $port,
            $errno,
            $errstr,
            $this->_fp_timeout
        ))
        {
            // socket connection succeeded

            return true;
        }
        else
        {
            // socket connection failed
            $this->status = $errno;
            switch($errno)
            {
            case -3:
                $this->error="socket creation failed (-3)";
            case -4:
                $this->error="dns lookup failure (-4)";
            case -5:
                $this->error="connection refused or timed out (-5)";
            default:
                $this->error="connection failed (".$errno.")";
            }
            return false;
        }
    }
/*======================================================================*\
    Function:	_disconnect
    Purpose:	disconnect a socket connection
    Input:		$fp	file pointer
\*======================================================================*/

    function _disconnect($fp)
    {
        return(fclose($fp));
    }


/*======================================================================*\
    Function:	_prepare_post_body
    Purpose:	Prepare post body according to encoding type
    Input:		$formvars  - form variables
                $formfiles - form upload files
    Output:		post body
\*======================================================================*/

    function _prepare_post_body($formvars, $formfiles)
    {
        settype($formvars, "array");
        settype($formfiles, "array");
        $postdata = '';

        if (count($formvars) == 0 && count($formfiles) == 0)
            return;

        switch ($this->_submit_type) {
        case "application/x-www-form-urlencoded":
            reset($formvars);
            foreach($formvars as $key => $val)
            {
                if (is_array($val) || is_object($val)) {
                    foreach($val as $cur_key => $cur_val)
                    {
                        $postdata .= urlencode($key)."[]=".urlencode($cur_val)."&";
                    }
                } else
                {
                    $postdata .= urlencode($key)."=".urlencode($val)."&";
                }
            }
            break;

        case "multipart/form-data":
            $this->_mime_boundary = "Snoopy".md5(uniqid(microtime()));

            reset($formvars);
            foreach($formvars as $key => $val)
            {
                if (is_array($val) || is_object($val)) {
                    foreach($val as $cur_key => $cur_val) {
                        $postdata .= "--".$this->_mime_boundary."\r\n";
                        $postdata .= "Content-Disposition: form-data; name=\"$key\[\]\"\r\n\r\n";
                        $postdata .= "$cur_val\r\n";
                    }
                } else {
                    $postdata .= "--".$this->_mime_boundary."\r\n";
                    $postdata .= "Content-Disposition: form-data; name=\"$key\"\r\n\r\n";
                    $postdata .= "$val\r\n";
                }
            }

            reset($formfiles);
            foreach($formfiles as $field_name => $file_names) {
                settype($file_names, "array");
                foreach($file_names as $file_name) {
                    if (!is_readable($file_name)) continue;

                    $fp = fopen($file_name, "r");
                    $file_content = fread($fp, filesize($file_name));
                    fclose($fp);
                    $base_name = basename($file_name);

                    $postdata .= "--".$this->_mime_boundary."\r\n";
                    $postdata .= "Content-Disposition: form-data; name=\"$field_name\"; filename=\"$base_name\"\r\n\r\n";
                    $postdata .= "$file_content\r\n";
                }
            }
            $postdata .= "--".$this->_mime_boundary."--\r\n";
            break;
        }

        return $postdata;
    }
    }
?>
